package com.informa.utils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Properties;

import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;

public class IOUtils {

	private static final String CLASSPATH_PREFIX = "classpath:";
	private static final String CLASSPATH_STAR_PREFIX = "classpath*:";
	private static final String FILE_PREFIX = "file:";
	private static final String URL_PREFIX = "url:";
	private static final String DEFAULT_CHARACTER_ENCODING = "UTF-8";
	
	public static Properties propertiesFrom(String classpathLocation) {
		Properties properties = new Properties();
		try {
			properties.load(inputStreamFor(classpathLocation));
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return properties;
	}
	
	public static byte [] bytesFrom(String location) {
		try {
			return FileCopyUtils.copyToByteArray(asResource(location).getInputStream());
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static byte [] bytesFrom(File file) {
		return bytesFrom(asResource(file));
	}
	
	public static byte [] bytesFrom(InputStream inputStream) {
		
		Assert.notNull(inputStream, "Must provide a non null InputStream");
		
		try {
			return FileCopyUtils.copyToByteArray(inputStream);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static InputStream inputStreamFor(String location) {
		try {
			return asResource(location).getInputStream();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static String textFrom(String location, String encoding) {
		return textFrom( location, encoding, true );
	}
	
	public static String textFrom(String location, String encoding, boolean normalised) {
		return buildString(bytesFrom( location ), encoding, normalised);
	}

	public static String normalise(String original) {
		return original
					.replace( "\r\n", "\n" )
					.replace( "\r", "\n" );
	}
	
	public static String textFrom(String location) {
		return textFrom(location, DEFAULT_CHARACTER_ENCODING);
	}
	
	public static String textFrom(InputStream in) {
		return buildString(bytesFrom( in ) );
	}
	
	public static byte [] bytesFrom(String location, Class<? extends Object> baseClass) {
		return bytesFrom(asResource(location, baseClass));
	}
	
	public static byte [] bytesFrom(Resource resource) {
		try {
			return FileCopyUtils.copyToByteArray(resource.getInputStream());
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static InputStream inputStreamFor(String location, Class<? extends Object> baseClass) {
		try {
			return asResource(location, baseClass).getInputStream();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static InputStream inputStreamFor(Resource resource) {
		try {
			return resource.getInputStream();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static String textFrom(String location, Class<? extends Object> baseClass, String encoding) {
		return buildString(bytesFrom(location, baseClass), encoding);
	}

	public static String textFrom(String location, Class<? extends Object> baseClass) {
		return textFrom( location, baseClass, DEFAULT_CHARACTER_ENCODING );
	}
	
	public static String textFrom(Resource resource, String encoding) {
		return buildString(bytesFrom(resource), encoding);
	}
	
	public static String textFrom(File file) {
		return textFrom(asResource( file ));
	}
	
	public static String textFrom(Resource resource) {
		return textFrom( resource, DEFAULT_CHARACTER_ENCODING );
	}
	
	public static Resource asResource(String location) {
		return new DefaultResourceLoader().getResource(location);
	}
	
	public static Resource asResource(File file) {
		return new FileSystemResource(file);
	}
	
	
	private static String buildString(byte[] bytes) {
		return buildString( bytes, DEFAULT_CHARACTER_ENCODING, true );
	}
	
	private static String buildString(byte[] bytes, String encoding) {
		return buildString( bytes, encoding, true );
	}	
	
	private static String buildString(byte[] bytes, String encoding,
			boolean normalised) {
		normalised = false;
		try {
			String text = new String(bytes, encoding ); 
			return normalised ? normalise( text ) : text;
		} catch (Exception e) {
			throw new RuntimeException( e );
		}
	}
	
	public static Resource asResource(String location, Class<? extends Object> baseClass) {
		String path = pathFrom(location);
		return new ClassPathResource(path, baseClass);
	}

	private static String pathFrom(String location) {
		String prefix = prefixFrom(location);
		String path = location.substring(prefix.length(), location.length());
		return path;
	}

	private static String prefixFrom(String location) {
		if (location.startsWith(CLASSPATH_STAR_PREFIX)) return CLASSPATH_STAR_PREFIX;
		if (location.startsWith(CLASSPATH_PREFIX)) return CLASSPATH_PREFIX;
		if (location.startsWith(URL_PREFIX)) return URL_PREFIX;
		if (location.startsWith(FILE_PREFIX)) return FILE_PREFIX;
		return "";
	}
	
	public static InputStream inputStreamForText(String text) {
		try {
			return new ByteArrayInputStream(text.getBytes("UTF-8"));
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static String asUtf8(byte [] bytes) {
		try {
			return new String(bytes, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new IllegalArgumentException(e);
		}
	}
	
	public static File asFile(String location) {
		
		try {
			return asResource( location ).getFile();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		
	}
	
	public static void write(byte [] bytes, String path) {
		write(bytes, path);
	}
	
	public static void write(byte [] bytes, File file) {
		try {
			FileCopyUtils.copy(bytes, file);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	

	public static void write(byte[] bytes, Resource resource) {
		try {
			write(bytes, resource.getFile());
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static void write(byte[] bytes, OutputStream out) {
		try {
			FileCopyUtils.copy(bytes, out);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static void writeAsUtf8Bytes(String utf8Text, OutputStream out) {
		try {
			write(utf8Text.getBytes( "UTF-8" ), out);
		} catch (UnsupportedEncodingException e) {
			throw new IllegalArgumentException(e);
		}
	}
	
}
